﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

using System;
using System.Collections;
using System.Collections.Generic;
using Tesla.Direct3D10.Graphics.Implementation;
using Tesla.Graphics;
using D3D = SlimDX.Direct3D10;

namespace Tesla.Direct3D10.Graphics {
    /// <summary>
    /// Concrete Direct3D10 implementation of <see cref="ISamplerStateCollection"/>.
    /// </summary>
    public sealed class D3D10SamplerStateCollection : ISamplerStateCollection {
        private bool _vertexSamplers;
        private int _maxSamplers;
        private SamplerState[] _samplers;
        private bool[] _dirtyMark;
        private bool _shortCircuitUpdate;
        private D3D10Renderer _renderer;
        private D3D.Device _graphicsDevice;

        /// <summary>
        /// Gets or sets the sampler state.
        /// </summary>
        /// <returns>The sampler state currently set on the device</returns>
        public SamplerState this[int index] {
            get {
                if(index < 0 || index >= _maxSamplers) {
                    throw new ArgumentOutOfRangeException("index", "Index is out of range");
                }
                D3D10Helper.CheckDisposed(_graphicsDevice);
                return _samplers[index];
            }
            set {
                if(index < 0 || index >= _maxSamplers) {
                    throw new ArgumentOutOfRangeException("index", "Index is out of range");
                }
                D3D10Helper.CheckDisposed(_graphicsDevice);
                SamplerState oldState = _samplers[index];

                //Filter out duplicates
                if(oldState == null) {
                    _samplers[index] = value;
                    _dirtyMark[index] = true;
                    _shortCircuitUpdate = true;
                } else if(oldState.RenderStateKey != value.RenderStateKey) {
                    _samplers[index] = value;
                    _dirtyMark[index] = true;
                    _shortCircuitUpdate = true;
                }
            }
        }

        /// <summary>
        /// Gets the maximum number of sampler states that can be set.
        /// </summary>
        public int MaxSamplers {
            get {
                return _maxSamplers;
            }
        }

        /// <summary>
        /// Creates a new instance of <see cref="D3D10SamplerStateCollection"/>.
        /// </summary>
        /// <param name="renderer">The D3D10 renderer.</param>
        /// <param name="maxSamplers">The max sampler count.</param>
        /// <param name="vertexSamplers">True if this is meant for vertex shader samplers, false for pixel shader samplers.</param>
        internal D3D10SamplerStateCollection(D3D10Renderer renderer, int maxSamplers, bool vertexSamplers) {
            _renderer = renderer;
            _graphicsDevice = renderer.GraphicsDevice;
            _maxSamplers = maxSamplers;
            _vertexSamplers = vertexSamplers;
            _samplers = new SamplerState[maxSamplers];
            _dirtyMark = new bool[maxSamplers];
            _shortCircuitUpdate = false;
        }

        /// <summary>
        /// Flushes the collection's state to the device, only samplers that have been
        /// changed since the last draw call will get sent.
        /// </summary>
        internal void FlushState() {
            //Return if we haven't even touched these states, so we don't have to loop over them.
            if(!_shortCircuitUpdate) {
                return;
            }
            for(int i = 0; i < _maxSamplers; i++) {
                if(_dirtyMark[i]) {
                    _dirtyMark[i] = false;

                    D3D.SamplerState ss = null;
                    SamplerState value = _samplers[i];
                    if(value != null) {
                        D3D10SamplerStateImplementation impl = value.Implementation as D3D10SamplerStateImplementation;
                        ss = impl.D3D10SamplerState;
                    }

                    if(_vertexSamplers) {
                        _graphicsDevice.VertexShader.SetSampler(ss, i);
                    } else {
                        _graphicsDevice.PixelShader.SetSampler(ss, i);
                    }
                }
            }
            _shortCircuitUpdate = false;
        }


        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<SamplerState> GetEnumerator() {
            return (IEnumerator<SamplerState>) _samplers.GetEnumerator();
        }



        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator() {
            return _samplers.GetEnumerator();
        }

    }
}
